﻿using Sidvall.IdentityManager.Security.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Sidvall.IdentityManager.Security
{
    public class UserStore :
        Microsoft.AspNet.Identity.IUserClaimStore<AspNetUsers, string>,
        Microsoft.AspNet.Identity.IUserEmailStore<AspNetUsers>,
        Microsoft.AspNet.Identity.IUserLockoutStore<AspNetUsers, string>,
        Microsoft.AspNet.Identity.IUserLoginStore<AspNetUsers>,
        Microsoft.AspNet.Identity.IUserPasswordStore<AspNetUsers>,
        Microsoft.AspNet.Identity.IUserPhoneNumberStore<AspNetUsers>,
        Microsoft.AspNet.Identity.IUserSecurityStampStore<AspNetUsers, string>,
        Microsoft.AspNet.Identity.IUserStore<AspNetUsers>,
        Microsoft.AspNet.Identity.IUserTwoFactorStore<AspNetUsers, string>
    {
        #region Public Members

        #region FindByIdAsync

        public async Task<AspNetUsers> FindByIdAsync(string userId)
        {
            var item = await Sidvall.IdentityManager.Security.SystemContext.Current.DataPortal.AspNetUsersManager.GetUserByIUserIdAsync(userId, true, true);
            return item;
        }

        #endregion
        #region FindByEmailAsync

        public async Task<AspNetUsers> FindByEmailAsync(string email)
        {
            var item = await Sidvall.IdentityManager.Security.SystemContext.Current.DataPortal.AspNetUsersManager.GetUserByEmailAsync(email, true, true);
            return item;
        }

        #endregion
        #region FindByNameAsync

        public async Task<AspNetUsers> FindByNameAsync(string userName)
        {
            var item = await Sidvall.IdentityManager.Security.SystemContext.Current.DataPortal.AspNetUsersManager.GetUserByUserNameAsync(userName, true, true);
            return item;
        }

        #endregion

        #region AddClaimAsync

        public async Task AddClaimAsync(AspNetUsers user, System.Security.Claims.Claim claim)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            if (claim == null)
                throw new System.ArgumentNullException("claim");
            await user.LoadAspNetUserClaimsAsync();
            var item = new AspNetUserClaims()
            {
                UserId = user.Id,
                ClaimType = claim.Type,
                ClaimValue = claim.Value,
            };
            user.AspNetUserClaims.Add(item);
        }

        #endregion
        #region AddLoginAsync

        public async Task AddLoginAsync(AspNetUsers user, Microsoft.AspNet.Identity.UserLoginInfo login)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            if (login == null)
                throw new System.ArgumentNullException("login");
            await user.LoadAspNetUserLoginsAsync();
            var item = new AspNetUserLogins()
            {
                UserId = user.Id,
                ProviderKey = login.ProviderKey,
                LoginProvider = login.LoginProvider
            };
            user.AspNetUserLogins.Add(item);
        }

        #endregion
        #region CreateAsync

        public async Task CreateAsync(AspNetUsers user)
        {
            if (user == null)
                throw new System.ArgumentNullException("user");
            user = await user.SaveItemAsync();
        }

        #endregion
        #region DeleteAsync

        public async Task DeleteAsync(AspNetUsers user)
        {
            if (user == null)
                throw new System.ArgumentNullException("user");
            await Sidvall.IdentityManager.Security.SystemContext.Current.DataPortal.AspNetUsersManager.DeleteItemByPrimaryKeyAsync(user.Id);
        }

        #endregion
        #region FindAsync

        public async Task<AspNetUsers> FindAsync(Microsoft.AspNet.Identity.UserLoginInfo login)
        {
            if (login == null)
                throw new System.ArgumentNullException("login");
            return (await Sidvall.IdentityManager.Security.SystemContext.Current.DataPortal.AspNetUsersManager.GetUsersFromLoginAsync(login.LoginProvider, login.ProviderKey, true, true)).Items.FirstOrDefault();
        }

        #endregion
        #region GetAccessFailedCountAsync

        public Task<int> GetAccessFailedCountAsync(AspNetUsers user)
        {
            if (user == null)
                throw new System.ArgumentNullException("user");
            return Task.FromResult(user.AccessFailedCount);
        }

        #endregion
        #region GetClaimsAsync

        public async Task<IList<System.Security.Claims.Claim>> GetClaimsAsync(AspNetUsers user)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            await user.LoadAspNetUserClaimsAsync();
            return user.AspNetUserClaims.Items.Select(c => new System.Security.Claims.Claim(c.ClaimType, c.ClaimValue)).ToList();
        }

        #endregion
        #region GetEmailAsync

        public Task<string> GetEmailAsync(AspNetUsers user)
        {
            if (user == null)
                throw new System.ArgumentNullException("user");
            return Task.FromResult(user.Email);
        }

        #endregion
        #region GetEmailConfirmedAsync

        public Task<bool> GetEmailConfirmedAsync(AspNetUsers user)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            return Task.FromResult(user.EmailConfirmed);
        }

        #endregion
        #region GetLockoutEnabledAsync

        public Task<bool> GetLockoutEnabledAsync(AspNetUsers user)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            return Task.FromResult(user.LockoutEnabled);
        }

        #endregion
        #region GetLockoutEndDateAsync

        public Task<System.DateTimeOffset> GetLockoutEndDateAsync(AspNetUsers user)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            if (user.LockoutEndDateUtc != null)
                return Task.FromResult(new System.DateTimeOffset(System.DateTime.SpecifyKind(user.LockoutEndDateUtc.Value, System.DateTimeKind.Utc)));
            return Task.FromResult(new System.DateTimeOffset());
        }

        #endregion
        #region GetLoginsAsync

        public async Task<IList<Microsoft.AspNet.Identity.UserLoginInfo>> GetLoginsAsync(AspNetUsers user)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            await user.LoadAspNetUserLoginsAsync();
            return user.AspNetUserLogins.Items.Select(l => new Microsoft.AspNet.Identity.UserLoginInfo(l.LoginProvider, l.ProviderKey)).ToList();
        }

        #endregion
        #region GetPasswordHashAsync

        public Task<string> GetPasswordHashAsync(AspNetUsers user)
        {
            if (user == null)
                throw new System.ArgumentNullException("user");
            return Task.FromResult(user.PasswordHash);
        }

        #endregion
        #region GetPhoneNumberAsync

        public Task<string> GetPhoneNumberAsync(AspNetUsers user)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            return Task.FromResult(user.PhoneNumber);
        }

        #endregion
        #region GetPhoneNumberConfirmedAsync

        public Task<bool> GetPhoneNumberConfirmedAsync(AspNetUsers user)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            return Task.FromResult(user.PhoneNumberConfirmed);
        }

        #endregion
        #region GetSecurityStampAsync

        public Task<string> GetSecurityStampAsync(AspNetUsers user)
        {
            if (user == null)
                throw new System.ArgumentNullException("user");
            return Task.FromResult(user.SecurityStamp);
        }

        #endregion
        #region GetTwoFactorEnabledAsync

        public Task<bool> GetTwoFactorEnabledAsync(AspNetUsers user)
        {
            if (user == null)
                throw new System.ArgumentNullException("user");
            return Task.FromResult(user.TwoFactorEnabled);
        }

        #endregion
        #region HasPasswordAsync

        public Task<bool> HasPasswordAsync(AspNetUsers user)
        {
            return Task.FromResult(!string.IsNullOrWhiteSpace(user.PasswordHash));
        }

        #endregion
        #region IncrementAccessFailedCountAsync

        public Task<int> IncrementAccessFailedCountAsync(AspNetUsers user)
        {
            if (user == null)
                throw new System.ArgumentNullException("user");
            user.AccessFailedCount += 1;
            return Task.FromResult(user.AccessFailedCount);
        }

        #endregion
        #region RemoveClaimAsync

        public async Task RemoveClaimAsync(AspNetUsers user, System.Security.Claims.Claim claim)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            if (claim == null)
                throw new System.ArgumentNullException("claim");
            await user.LoadAspNetUserClaimsAsync();
            var item = (from o in user.AspNetUserClaims.Items
                        where o.ClaimType.Equals(claim.Type, System.StringComparison.OrdinalIgnoreCase) && o.ClaimValue.Equals(claim.Value, System.StringComparison.OrdinalIgnoreCase)
                        select o).FirstOrDefault();
            if (item != null)
                user.AspNetUserClaims.Remove(item);
        }

        #endregion
        #region RemoveLoginAsync

        public async Task RemoveLoginAsync(AspNetUsers user, Microsoft.AspNet.Identity.UserLoginInfo login)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            if (login == null)
                throw new System.ArgumentNullException("login");
            await user.LoadAspNetUserLoginsAsync();
            var item = (from o in user.AspNetUserLogins.Items
                        where o.LoginProvider.Equals(login.LoginProvider, System.StringComparison.OrdinalIgnoreCase) && o.ProviderKey.Equals(login.ProviderKey, System.StringComparison.OrdinalIgnoreCase)
                        select o).FirstOrDefault();
            if (item != null)
                user.AspNetUserLogins.Remove(item);
        }

        #endregion
        #region ResetAccessFailedCountAsync

        public Task ResetAccessFailedCountAsync(AspNetUsers user)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            user.AccessFailedCount = 0;
            return Task.FromResult(0);
        }

        #endregion
        #region SetEmailAsync

        public Task SetEmailAsync(AspNetUsers user, string email)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            user.Email = email;
            return Task.FromResult(0);
        }

        #endregion
        #region SetEmailConfirmedAsync

        public Task SetEmailConfirmedAsync(AspNetUsers user, bool confirmed)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            user.EmailConfirmed = confirmed;
            return Task.FromResult(0);
        }

        #endregion
        #region SetLockoutEnabledAsync

        public Task SetLockoutEnabledAsync(AspNetUsers user, bool enabled)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            user.LockoutEnabled = enabled;
            return Task.FromResult(0);
        }

        #endregion
        #region SetLockoutEndDateAsync

        public Task SetLockoutEndDateAsync(AspNetUsers user, System.DateTimeOffset lockoutEnd)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            if (lockoutEnd != System.DateTimeOffset.MinValue)
                user.LockoutEndDateUtc = lockoutEnd.UtcDateTime;
            else
                user.LockoutEndDateUtc = null;
            return Task.FromResult(0);
        }

        #endregion
        #region SetPasswordHashAsync

        public Task SetPasswordHashAsync(AspNetUsers user, string passwordHash)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            user.PasswordHash = passwordHash;
            return Task.FromResult(0);
        }

        #endregion
        #region SetPhoneNumberAsync

        public Task SetPhoneNumberAsync(AspNetUsers user, string phoneNumber)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            user.PhoneNumber = phoneNumber;
            return Task.FromResult(0);
        }

        #endregion
        #region SetPhoneNumberConfirmedAsync

        public Task SetPhoneNumberConfirmedAsync(AspNetUsers user, bool confirmed)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            user.PhoneNumberConfirmed = confirmed;
            return Task.FromResult(0);
        }

        #endregion
        #region SetSecurityStampAsync

        public Task SetSecurityStampAsync(AspNetUsers user, string stamp)
        {
            if (user == null)
                throw new System.ArgumentNullException("user");
            user.SecurityStamp = stamp;
            return Task.FromResult(0);
        }

        #endregion
        #region SetTwoFactorEnabledAsync

        public Task SetTwoFactorEnabledAsync(AspNetUsers user, bool enabled)
        {

            if (user == null)
                throw new System.ArgumentNullException("user");
            user.TwoFactorEnabled = enabled;
            return Task.FromResult(0);
        }

        #endregion
        #region UpdateAsync

        public async Task UpdateAsync(AspNetUsers user)
        {
            if (user == null)
                throw new System.ArgumentNullException("user");
            user = await user.SaveItemAsync();
        }

        #endregion

        #region Dispose

        public void Dispose()
        {
        }

        #endregion

        #endregion
    }
}